package org.new9uy.tetris.model;

import org.new9uy.tetris.GameConstants;

public class Gravity extends Thread
{
    private boolean pause;
    private boolean speed;
    private boolean cancelled = false;
    
    private int lvl;
    private int delay; //speed of the gravity.
    private int actualDelay;
    
    private Next next;
    private Score scr;
    private final Matrix matrix;

    
    private boolean ignoreKeys = false;

    public Gravity(int lvl, Matrix matrix, Next next, Score scr)
    {
        this.lvl = lvl;
        this.delay = delayForLvl(lvl);
        actualDelay = delay;
        this.matrix = matrix;
        this.next = next;
        this.scr = scr;
        this.speed = false;
        this.pause = true;
    }

    public Score getScore()
    {
        return scr;
    }

    public void setScore(Score scr)
    {
        this.scr = scr;
    }

    public Matrix getMatrix()
    {
        return matrix;
    }

    public int getLvl()
    {
        return lvl;
    }

    public void lvlUp()
    {
        if (lvl < 10)
        {
            delay = delayForLvl(++lvl);
        }
    }

    public void setLevel(int lvl)
    {
        this.lvl = lvl;
        delay = delayForLvl(lvl);
    }

    public void pause(boolean pause)
    {
        this.pause = pause;
    }

    public boolean getPause()
    {
        return pause;
    }

    public boolean getIgnoreKeys()
    {
        return ignoreKeys;
    }

    public void reset()
    {
        pause(true);
        lvl = 0;
        delay = GameConstants.GRAVITY_DEFAULT_DELAY;
        actualDelay = delay;
        matrix.reset();
        pause(false);
        matrix.spawnTetrimino(next.getNext());
    }

    public void cancel()
    {
        pause = true;
        cancelled = true;
        interrupt();
    }

    @Override
    public void run()
    {
        while (!cancelled)
        {
            if (matrix.isOver())
            {
                pause = true;
            }
            try
            {
                sleep(actualDelay);
                if (!pause)
                {
                    if (!fall())
                    {
                        land();
                    }
                }
            } catch (InterruptedException e)
            {
            }
        }
    }

    private void land()
    {
        ignoreKeys = true;

        //Once the current tetrimino lands, no other thread
        //can be allowed to use the matrix until the next tetrimino
        //spawns.
        synchronized (matrix)
        {
            if (matrix.isOver())
            {
                pause = true;
                return;
            }
            matrix.lockCurrent();
            scr.checkLines(matrix.current, getLvl());
            //checkLevel();
            matrix.spawnTetrimino(next.getNext());
            actualDelay = delay;
        }

        ignoreKeys = false;
    }

    private boolean fall()
    {
        return matrix.moveTetrimino('y', -1);
    }

    //@Note: Level increase is 1 for every 10 lines.
    public void checkLevel()
    {
        if (scr.totalLinesDone() / 10 > lvl)
        {
            lvlUp();
        }
    }

    //-----------------------------------------------------------------
    // Speed control functions
    //-----------------------------------------------------------------
    public void drop()
    {
        if (ignoreKeys)
        {
            return;
        }
        matrix.dropTetrimino();
        actualDelay = 0;
        interrupt();
    }

    public void speedUp()
    {
        if (ignoreKeys)
        {
            return;
        }
        interrupt();
        actualDelay = GameConstants.GRAVITY_SPEEDUP_DELAY;
        speed = true;
    }

    public void slowDown()
    {
        if (ignoreKeys)
        {
            return;
        }
        interrupt();
        actualDelay = delay;
        speed = false;
    }

    public void changeSpeed()
    {
        if (speed)
        {
            slowDown();
        } else
        {
            speedUp();
        }
    }

    public static int delayForLvl(int lvl)
    {
        return GameConstants.GRAVITY_DEFAULT_DELAY
                - (lvl * GameConstants.GRAVITY_REDUCTION_PER_LEVEL);
    }

    public void refreshLevel(byte lvl)
    {
        this.lvl = lvl;
    }
}
